package ElectricCraft.viewers;

import ElectricCraft.drawer.Drawer;
import ElectricCraft.model.ChipElement;
import ElectricCraft.model.ChipModel;

import java.awt.*;
import java.util.LinkedList;
import java.util.List;

/**
 * View class shows ChipModel state.
 */
public class ChipViewer {
	protected ChipModel model;
	public void setModel(ChipModel model) {
		this.model = model;
	}

	protected Drawer drawer;
	public void setDrawer(Drawer drawer) {
		this.drawer = drawer;
	}

	public void getElementsRange(Rectangle range, Point gridOffset) {
		int itemSize = drawer.getItemSize();

		int startX, countX, gridOffsetX;
		if (offsetX < 0) {
			startX = 0;
			gridOffsetX = -offsetX;
			countX = Math.min((sizeX - gridOffsetX + itemSize - 1) / itemSize, model.elements.length);
		} else {
			startX = Math.min(offsetX / itemSize, model.elements.length);
			countX = Math.min((sizeX + itemSize - 1) / itemSize + 1, model.elements.length - startX);
			gridOffsetX = -offsetX % itemSize;
		}

		int startY, countY, gridOffsetY;
		if (offsetY < 0) {
			startY = 0;
			gridOffsetY = -offsetY;
			countY = Math.min((sizeY - gridOffsetY + itemSize - 1) / itemSize, model.elements.length);
		} else {
			startY = Math.min(offsetY / itemSize, model.elements.length);
			countY = Math.min((sizeY + itemSize - 1) / itemSize + 1, model.elements.length - startY);
			gridOffsetY = -offsetY % itemSize;
		}

		range.setBounds(startX, startY, countX, countY);
		gridOffset.setLocation(gridOffsetX, gridOffsetY);
	}

	private int offsetX = 0;
	private int offsetY = 0;
	private int sizeX   = 0;
	private int sizeY   = 0;

	protected final Rectangle elementsRange = new Rectangle();
	protected final Point gridOffset = new Point();
	public void draw() {
		int itemSize = drawer.getItemSize();

		drawer.setClipRect(0, 0, sizeX, sizeY);

		// grid
		getElementsRange(elementsRange, gridOffset);

		drawer.pushTransform(gridOffset.x, gridOffset.y);

		drawer.setDrawPos(0, 0);
		drawer.drawGrid(elementsRange.width, elementsRange.height);

		drawElements(model.elements, elementsRange, itemSize);
		drawer.popTransform();
	}

	protected void drawElements(ChipElement [][] elements, Rectangle range, int itemSize) {
		// draw items
		for (int x = 0; x < range.width; ++x) {
			for (int y = 0; y < range.height; ++y) {
				ChipElement elem = elements[range.x + x][range.y + y];
				if (elem == null) {
					continue;
				}

				drawer.setDrawPos(
					x * itemSize,
					y * itemSize);

				switch (elem.type) {
					case ChipElement.ELEMENT_WIRE:
                    {
                        int mask = 0;
                        if (model.state != null) {
                            int group = elem.wireGroups[0];
                            if (group != -1 && model.state.groups[group]) {
                                mask |= 1;
                            }
                            group = elem.wireGroups[1];
                            if (group != -1 && model.state.groups[group]) {
                                mask |= 2;
                            }
                        }

						drawer.drawWire(elem.itemType, mask);
                    }
					break;
					case ChipElement.ELEMENT_KEY:
                    {
                        boolean isOpened = (elem.itemType != 0);
                        if (model.state != null) {
                            isOpened = model.state.keys[elem.wireGroups[0]];
                        }

						drawer.drawKey(elem.orientation, isOpened);
                    }
					break;
					case ChipElement.ELEMENT_DIODE:
						drawer.drawDiode(elem.orientation);
						break;
					case ChipElement.ELEMENT_POWER:
						drawer.drawPower();
						break;
					case ChipElement.ELEMENT_INPUT:
						drawer.drawInput(elem.itemType);
						break;
					case ChipElement.ELEMENT_OUTPUT:
						drawer.drawOutput(elem.itemType);
						break;
					case ChipElement.ELEMENT_CHIP_PART:
                        if (elem.itemType == -1) {
                            // preview chip
                            drawer.drawPreviewChip();
                        } else {
                            drawer.drawChipPart(elem.orientation);
                        }
				}
			}
		}
	}

	public void setViewport(int offsetX, int offsetY, int sizeX, int sizeY) {
		this.offsetX    = offsetX;
		this.offsetY    = offsetY;
		this.sizeX      = sizeX;
		this.sizeY      = sizeY;
	}

    private final List<NotificationListener> viewUpdatedListeners = new LinkedList<NotificationListener>();
    public void addViewUpdatedListener(NotificationListener listener) {
        viewUpdatedListeners.add(listener);
    }

    public void updateView() {
        for (NotificationListener listener : viewUpdatedListeners) {
            listener.notified();
        }
    }

    protected final Point elementIndex = new Point();
    public boolean getElementByPos(int x, int y, Point elementIndex) {
        getElementsRange(elementsRange, gridOffset);

        if (elementsRange.width * elementsRange.height == 0) {
            return false;
        }

        if (x < 0 || y < 0) {
            return false;
        }
        if (x < gridOffset.x || y < gridOffset.y) {
            return false;
        }

        int itemSize = drawer.getItemSize();

        if (x > gridOffset.x + elementsRange.width * itemSize) {
            return false;
        }
        if (y > gridOffset.y + elementsRange.height * itemSize) {
            return false;
        }

        elementIndex.x = elementsRange.x + (x - gridOffset.x) / itemSize;
        elementIndex.y = elementsRange.y + (y - gridOffset.y) / itemSize;

        return true;
    }

    public int getChipUnderCursor(int x, int y) {
        if (!getElementByPos(x, y, elementIndex)) {
            return -1;
        }

        ChipElement elem = model.elements[elementIndex.x][elementIndex.y];
        if (elem == null || elem.type != ChipElement.ELEMENT_CHIP_PART) {
            return -1;
        }

        return elem.itemType;
    }
}
